import 'package:agora_rtm/src/impl/gen/rtm_result_handler.dart';
import 'package:agora_rtm/src/impl/extensions.dart';
import 'package:agora_rtm/src/binding_forward_export.dart';
import 'package:agora_rtm/src/bindings/gen/agora_rtm_client_impl.dart'
    as native_binding;
import 'package:flutter/services.dart';

import '../rtm_result_handler_impl.dart';
import 'agora_rtm_lock_ohos_impl.dart';
import 'agora_rtm_presence_ohos_impl.dart';
import 'agora_rtm_storage_ohos_impl.dart';
import 'agora_stream_channel_ohos_impl.dart';

class RtmClientOhosImpl implements RtmClient {
  RtmClientOhosImpl._(this.rtmResultHandler){
    if (rtmMethodChannel == null) {
      rtmMethodChannel = const MethodChannel("rmt/ohos");
      rtmMethodChannel!.setMethodCallHandler((call) async {
        if (call.method == 'onMessageEvent') {
          MessageEvent? event = MessageEvent.fromJson(jsonDecode(call.arguments['event']));
          rtmResultHandler.rtmEventHandler.onMessageEvent!(event);
          return true;
        }else if (call.method == 'onPresenceEvent') {
          PresenceEvent? event = PresenceEvent.fromJson(jsonDecode(call.arguments['event']));
          rtmResultHandler.rtmEventHandler.onPresenceEvent!(event);
          return true;
        }else if (call.method == 'onTopicEvent') {
          TopicEvent? event = TopicEvent.fromJson(jsonDecode(call.arguments['event']));
          rtmResultHandler.rtmEventHandler.onTopicEvent!(event);
          return true;
        }else if (call.method == 'onLockEvent') {
          LockEvent? event = LockEvent.fromJson(jsonDecode(call.arguments['event']));
          rtmResultHandler.rtmEventHandler.onLockEvent!(event);
          return true;
        }else if (call.method == 'onStorageEvent') {
          StorageEvent? event = StorageEvent.fromJson(jsonDecode(call.arguments['event']));
          rtmResultHandler.rtmEventHandler.onStorageEvent!(event);
          return true;
        }else if (call.method == 'onJoinResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          String userId = call.arguments['userId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onJoinResult!(int.parse(requestId),channelName,userId,errorType);
          return true;
        }else if (call.method == 'onLeaveResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          String userId = call.arguments['userId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onLeaveResult!(int.parse(requestId),channelName,userId,errorType);
          return true;
        }else if (call.method == 'onJoinTopicResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          String userId = call.arguments['userId'];
          String topic = call.arguments['topic'];
          String metadata = call.arguments['metadata'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onJoinTopicResult!(int.parse(requestId),channelName,userId,topic,metadata,errorType);
          return true;
        }else if (call.method == 'onLeaveTopicResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          String userId = call.arguments['userId'];
          String topic = call.arguments['topic'];
          String metadata = call.arguments['metadata'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onLeaveTopicResult!(int.parse(requestId),channelName,userId,topic,metadata,errorType);
          return true;
        }else if (call.method == 'onSubscribeTopicResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          String userId = call.arguments['userId'];
          String topic = call.arguments['topic'];
          UserList succeedUsers = UserList.fromJson(call.arguments['succeedUsers']);
          UserList failedUsers = UserList.fromJson(call.arguments['failedUsers']);
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onSubscribeTopicResult!(int.parse(requestId),channelName,userId,topic,succeedUsers,failedUsers,errorType);
          return true;
        }else if (call.method == 'onConnectionStateChanged') {
          String channelName = call.arguments['channelName'];
          int state = call.arguments['state'];
          RtmConnectionState stateType = RtmConnectionState.connecting;
          for (var e in RtmConnectionState.values) {
            if (e.value() == state) {
              stateType = e;
              break;
            }
          }
          int reason = call.arguments['reason'];
          RtmConnectionChangeReason reasonType = RtmConnectionChangeReason.connecting;
          for (var e in RtmConnectionChangeReason.values) {
            if (e.value() == reason) {
              reasonType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onConnectionStateChanged!(channelName,stateType,reasonType);
          return true;
        }else if (call.method == 'onTokenPrivilegeWillExpire') {
          String channelName = call.arguments['channelName'];
          rtmResultHandler.rtmEventHandler.onTokenPrivilegeWillExpire!(channelName);
          return true;
        }else if (call.method == 'onSubscribeResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onSubscribeResult!(int.parse(requestId),channelName,errorType);
          return true;
        }else if (call.method == 'onPublishResult') {
          String requestId = call.arguments['requestId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onPublishResult!(int.parse(requestId),errorType);
          return true;
        }else if (call.method == 'onLoginResult') {
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          // todo ohos回调中没有requestId
          rtmResultHandler.rtmEventHandler.onLoginResult!(-1,errorType);
          return true;
        }else if (call.method == 'onSetChannelMetadataResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onSetChannelMetadataResult!(int.parse(requestId),channelName,channelTypeType,errorType);
          return true;
        }else if (call.method == 'onUpdateChannelMetadataResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onUpdateChannelMetadataResult!(int.parse(requestId),channelName,channelTypeType,errorType);
          return true;
        }else if (call.method == 'onRemoveChannelMetadataResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onRemoveChannelMetadataResult!(int.parse(requestId),channelName,channelTypeType,errorType);
          return true;
        }else if (call.method == 'onGetChannelMetadataResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          Metadata metadata = Metadata.fromJson(jsonDecode(call.arguments['metadata']));
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onGetChannelMetadataResult!(int.parse(requestId),channelName,channelTypeType,metadata,errorType);
          return true;
        }else if (call.method == 'onSetUserMetadataResult') {
          String requestId = call.arguments['requestId'];
          String userId = call.arguments['userId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onSetUserMetadataResult!(int.parse(requestId),userId,errorType);
          return true;
        }else if (call.method == 'onUpdateUserMetadataResult') {
          String requestId = call.arguments['requestId'];
          String userId = call.arguments['userId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onUpdateUserMetadataResult!(int.parse(requestId),userId,errorType);
          return true;
        }else if (call.method == 'onRemoveUserMetadataResult') {
          String requestId = call.arguments['requestId'];
          String userId = call.arguments['userId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onRemoveUserMetadataResult!(int.parse(requestId),userId,errorType);
          return true;
        }else if (call.method == 'onGetUserMetadataResult') {
          String requestId = call.arguments['requestId'];
          String userId = call.arguments['userId'];
          Metadata metadata = Metadata.fromJson(jsonDecode(call.arguments['metadata']));
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onGetUserMetadataResult!(int.parse(requestId),userId,metadata,errorType);
          return true;
        }else if (call.method == 'onSubscribeUserMetadataResult') {
          String requestId = call.arguments['requestId'];
          String userId = call.arguments['userId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onSubscribeUserMetadataResult!(int.parse(requestId),userId,errorType);
          return true;
        }else if (call.method == 'onSetLockResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          String lockName = call.arguments['lockName'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onSetLockResult!(int.parse(requestId),channelName,channelTypeType,lockName,errorType);
          return true;
        }else if (call.method == 'onRemoveLockResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          String lockName = call.arguments['lockName'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onRemoveLockResult!(int.parse(requestId),channelName,channelTypeType,lockName,errorType);
          return true;
        }else if (call.method == 'onReleaseLockResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          String lockName = call.arguments['lockName'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onReleaseLockResult!(int.parse(requestId),channelName,channelTypeType,lockName,errorType);
          return true;
        }else if (call.method == 'onAcquireLockResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          String lockName = call.arguments['lockName'];
          String errorDetails = call.arguments['errorDetails'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onAcquireLockResult!(int.parse(requestId),channelName,channelTypeType,lockName,errorType,errorDetails);
          return true;
        }else if (call.method == 'onRevokeLockResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          String lockName = call.arguments['lockName'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onRevokeLockResult!(int.parse(requestId),channelName,channelTypeType,lockName,errorType);
          return true;
        }else if (call.method == 'onGetLocksResult') {
          String requestId = call.arguments['requestId'];
          String channelName = call.arguments['channelName'];
          int channelType = call.arguments['channelType'];
          RtmChannelType channelTypeType = RtmChannelType.none;
          for (var e in RtmChannelType.values) {
            if (e.value() == channelType) {
              channelTypeType = e;
              break;
            }
          }
          List<LockDetail>? lockDetailList = (call.arguments['lockDetailList'] as List<dynamic>?)
              ?.map((e) => LockDetail.fromJson(e as Map<String, dynamic>))
              .toList();
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onGetLocksResult!(int.parse(requestId),channelName,channelTypeType,lockDetailList ??[],lockDetailList?.length?? 0,errorType);
          return true;
        }else if (call.method == 'onWhoNowResult') {
          String requestId = call.arguments['requestId'];
          String nextPage = call.arguments['nextPage'];
          int count = call.arguments['count'];
          List<UserState>? userSateList = (call.arguments['userSateList'] as List<dynamic>?)
              ?.map((e) {
                return UserState.fromJson(e as Map<String, dynamic>);
          })
              .toList();
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onWhoNowResult!(int.parse(requestId),userSateList?? [],count,nextPage,errorType);
          return true;
        }else if (call.method == 'onGetOnlineUsersResult') {
          String requestId = call.arguments['requestId'];
          String nextPage = call.arguments['nextPage'];
          int count = call.arguments['count'];
          List<UserState>? userSateList = (call.arguments['userSateList'] as List<dynamic>?)
              ?.map((e) => UserState.fromJson(e as Map<String, dynamic>))
              .toList();
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onGetOnlineUsersResult!(int.parse(requestId),userSateList?? [],count,nextPage,errorType);
          return true;
        }else if (call.method == 'onWhereNowResult') {
          String requestId = call.arguments['requestId'];
          List<ChannelInfo>? channels = (call.arguments['channels'] as List<dynamic>?)
              ?.map((e) => ChannelInfo.fromJson(e as Map<String, dynamic>))
              .toList();
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onWhereNowResult!(int.parse(requestId),channels?? [],channels?.length?? 0 ,errorType);
          return true;
        }else if (call.method == 'onGetUserChannelsResult') {
          String requestId = call.arguments['requestId'];
          ChannelInfo channels = ChannelInfo.fromJson(call.arguments['channels']);
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onGetUserChannelsResult!(int.parse(requestId),channels,1 ,errorType);
          return true;
        }else if (call.method == 'onPresenceSetStateResult') {
          String requestId = call.arguments['requestId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onPresenceSetStateResult!(int.parse(requestId),errorType);
          return true;
        }else if (call.method == 'onPresenceRemoveStateResult') {
          String requestId = call.arguments['requestId'];
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onPresenceRemoveStateResult!(int.parse(requestId),errorType);
          return true;
        }else if (call.method == 'onPresenceGetStateResult') {
          String requestId = call.arguments['requestId'];
          UserState state = UserState.fromJson(jsonDecode(call.arguments['state']));
          int errorCode = call.arguments['errorCode'];
          RtmErrorCode errorType = RtmErrorCode.ok;
          for (var e in RtmErrorCode.values) {
            if (e.value() == errorCode) {
              errorType = e;
              break;
            }
          }
          rtmResultHandler.rtmEventHandler.onPresenceGetStateResult!(int.parse(requestId),state,errorType);
          return true;
        }
        return false;
      });
    }
  }

  MethodChannel? rtmMethodChannel;
  static RtmClientOhosImpl? _instance;
  final RtmResultHandler rtmResultHandler;


  static Future<(RtmStatus, RtmClient)> create(
    String appId,
    String userId, {
    RtmConfig? config,
  }) async {


    final Map<String, dynamic> args = {
      "appId": appId,
      "userId": userId,
      "areaCode": config?.areaCode?.first.value() ?? 1,
      "presenceTimeoutInSec": config?.presenceTimeout ?? 300,
      "useStringUserId": config?.useStringUserId ?? true,
      "logConfig":
          config?.logConfig != null ? jsonEncode(config?.logConfig) : "",
      "proxyConfig":
          config?.proxyConfig != null ? jsonEncode(config?.proxyConfig) : "",
      "encryptionConfig": config?.encryptionConfig != null
          ? jsonEncode(config?.encryptionConfig)
          : "",
    };


    _instance ??= RtmClientOhosImpl._(RtmResultHandlerImpl());
    RtmStatus? rtmStatus = await _instance!.initialize(args);
    return (rtmStatus!, _instance!);
  }

  Future<RtmStatus?> initialize(Map<String, dynamic> args) async {
    int? code = await rtmMethodChannel?.invokeMethod<int>("initialize", args);
    return code != null ? getRtmStatus(code) : null;
  }

  static RtmStatus getRtmStatus(int code) {
    bool error = true;
    String reason = "";
    String operation = "";
    switch(code) {
      case 0:
        error = false;
        reason = "success";
        break;
      case -10001:
        reason = "The SDK is not initialized";
        break;
      case -10002:
        reason = "The user didn't login the RTM system";
        break;
      case -10003:
        reason = "The app ID is invalid";
        break;
      case -10004:
        reason = "The event handler is invalid";
        break;
      case -10005:
        reason = "The token is invalid";
        break;
      case -10006:
        reason = "The user ID is invalid";
        break;
      case -10007:
        reason = "The service is not initialized";
        break;
      case -10008:
        reason = "The channel name is invalid";
        break;
      case -10009:
        reason = "The token has expired";
        break;
      case -10010:
        reason = "There is no server resources now";
        break;
      case -10011:
        reason = "The login timeout";
        break;
      case -10012:
        reason = "The login is rejected by server";
        break;
      case -10013:
        reason = "The login is aborted due to unrecoverable error";
        break;
      case -10014:
        reason = "The parameter is invalid";
        break;
      case -10015:
        reason = "The login is not authorized. Happens user login the RTM system without granted from console";
        break;
      case -10016:
        reason = "Try to login or join with inconsistent app ID";
        break;
      case -10017:
        reason = "Already call same request";
        break;
      case -10018:
        reason = "Already call destroy or release, this instance is forbidden to call any api, please create new instance";
        break;
      case -10019:
        reason = "The channel type is invalid";
        break;
      case -10020:
        reason = "The encryption parameter is invalid";
        break;
      case -10021:
        reason = "The operation is too frequent";
        break;
      case -11001:
        reason = "The user has not joined the channel";
        break;
      case -11002:
        reason = "The user has not subscribed the channel";
        break;
      case -11003:
        reason = "The topic member count exceeds the limit";
        break;
      case -11004:
        reason = "The channel is reused in RTC";
        break;
      case -11005:
        reason = "The channel instance count exceeds the limit";
        break;
      case -11006:
        reason = "The channel is in error state";
        break;
      case -11007:
        reason = "The channel join failed";
        break;
      case -11008:
        reason = "The topic name is invalid";
        break;
      case -11009:
        reason = "The message is invalid";
        break;
      case -11010:
        reason = "The message length exceeds the limit";
        break;
      case -11011:
        reason = "The user list is invalid";
        break;
      case -11012:
        reason = "The stream channel is not available";
        break;
      case -11013:
        reason = "The topic is not subscribed";
        break;
      case -11014:
        reason = "The topic count exceeds the limit";
        break;
      case -11015:
        reason = "Join topic failed";
        break;
      case -11016:
        reason = "The topic is not joined";
        break;
      case -11017:
        reason = "The topic does not exist";
        break;
      case -11018:
        reason = "The topic meta is invalid";
        break;
      case -11019:
        reason = "Subscribe channel timeout";
        break;
      case -11020:
        reason = "Subscribe channel too frequent";
        break;
      case -11021:
        reason = "Subscribe channel failed";
        break;
      case -11022:
        reason = "Unsubscribe channel failed";
        break;
      case -11023:
        reason = "Encrypt message failed";
        break;
      case -11024:
        reason = "Publish message failed";
        break;
      case -11025:
        reason = "Publish message too frequent";
        break;
      case -11026:
        reason = "Publish message timeout";
        break;
      case -11027:
        reason = "The connection state is invalid";
        break;
      case -11028:
        reason = "Leave channel failed";
        break;
      case -11029:
        reason = "The custom type length exceeds the limit";
        break;
      case -11030:
        reason = "The custom type is invalid";
        break;
      case -11031:
        reason = "unsupported message type (in MacOS/iOS platform，message only support NSString and NSData)";
        break;
      case -11032:
        reason = "The channel presence is not ready";
        break;
      case -11033:
        reason = "The destination user of publish message is offline";
        break;
      case -12001:
        reason = "The storage operation failed";
        break;
      case -12002:
        reason = "The metadata item count exceeds the limit";
        break;
      case -12003:
        reason = "The metadata item is invalid";
        break;
      case -12004:
        reason = "The argument in storage operation is invalid";
        break;
      case -12005:
        reason = "The revision in storage operation is invalid";
        break;
      case -12006:
        reason = "The metadata length exceeds the limit";
        break;
      case -12007:
        reason = "The lock name in storage operation is invalid";
        break;
      case -12008:
        reason = "The lock in storage operation is not acquired";
        break;
      case -12009:
        reason = "The metadata key is invalid";
        break;
      case -12010:
        reason = "The metadata value is invalid";
        break;
      case -12011:
        reason = "The metadata key length exceeds the limit";
        break;
      case -12012:
        reason = "The metadata value length exceeds the limit";
        break;
      case -12013:
        reason = "The metadata key already exists";
        break;
      case -12014:
        reason = "The revision in storage operation is outdated";
        break;
      case -12015:
        reason = "The storage operation performed without subscribing";
        break;
      case -12016:
        reason = "The metadata item is invalid";
        break;
      case -12017:
        reason = "The user count exceeds the limit when try to subscribe";
        break;
      case -12018:
        reason = "The storage operation timeout";
        break;
      case -12019:
        reason = "The storage service not available";
        break;
      case -13001:
        reason = "The user is not connected";
        break;
      case -13002:
        reason = "The presence is not writable";
        break;
      case -13003:
        reason = "The argument in presence operation is invalid";
        break;
      case -13004:
        reason = "The cached presence state count exceeds the limit";
        break;
      case -13005:
        reason = "The state count exceeds the limit";
        break;
      case -13006:
        reason = "The state key is invalid";
        break;
      case -13007:
        reason = "The state value is invalid";
        break;
      case -13008:
        reason = "The state key length exceeds the limit";
        break;
      case -13009:
        reason = "The state value length exceeds the limit";
        break;
      case -13010:
        reason = "The state key already exists";
        break;
      case -13011:
        reason = "The user is not exist";
        break;
      case -13012:
        reason = "The presence operation timeout";
        break;
      case -13013:
        reason = "The presence operation failed";
        break;
      case -14001:
        reason = "The lock operation failed";
        break;
      case -14002:
        reason = "The lock operation timeout";
        break;
      case -14003:
        reason = "The lock operation is performing";
        break;
      case -14004:
        reason = "The lock already exists";
        break;
      case -14005:
        reason = "The lock name is invalid";
        break;
      case -14006:
        reason = "The lock is not acquired";
        break;
      case -14007:
        reason = "Acquire lock failed";
        break;
      case -14008:
        reason = "The lock is not exist";
        break;
      case -14009:
        reason = "The lock service is not available";
        break;
    }
    return RtmStatus(error, code.toString(), operation, reason);
  }

  void _setListenerIfNeeded(String key, Object? listener) {
    if (listener != null) {
      (rtmResultHandler as RtmResultHandlerImpl).setListener(key, listener);
    }
  }

  void _removeListenerIfNeeded(String key, Object? listener) {
    if (listener != null) {
      (rtmResultHandler as RtmResultHandlerImpl).removeListener(key);
    }
  }

  @override
  void addListener(
      {void Function(LinkStateEvent event)? linkState,
      void Function(MessageEvent event)? message,
      void Function(PresenceEvent event)? presence,
      void Function(TopicEvent event)? topic,
      void Function(LockEvent event)? lock,
      void Function(StorageEvent event)? storage,
      void Function(TokenEvent event)? token}) {
    _setListenerIfNeeded('linkState', linkState);
    _setListenerIfNeeded('message', message);
    _setListenerIfNeeded('presence', presence);
    _setListenerIfNeeded('topic', topic);
    _setListenerIfNeeded('lock', lock);
    _setListenerIfNeeded('storage', storage);
    _setListenerIfNeeded('token', token);
  }

  @override
  Future<(RtmStatus, StreamChannel?)> createStreamChannel(String channelName) async {
    final Map<String, dynamic> args = {
      "channelName": channelName,
    };
    await rtmMethodChannel?.invokeMethod("createStreamChannel", args);
    StreamChannelOhosImpl streamChannelOhosImpl = StreamChannelOhosImpl(rtmMethodChannel!,rtmResultHandler);
    return (const RtmStatus.success(operation: "success"), streamChannelOhosImpl);
  }

  @override
  RtmHistory getHistory() {
    // TODO: implement getHistory
    throw UnimplementedError();
  }

  @override
  RtmLock getLock() {
    final Map<String, dynamic> args = {
    };
    rtmMethodChannel?.invokeMethod("getLock", args);
    RtmLockOhosImpl rtmLockOhosImpl = RtmLockOhosImpl(rtmMethodChannel!,rtmResultHandler);
    return rtmLockOhosImpl;
  }

  @override
  RtmPresence getPresence() {
    final Map<String, dynamic> args = {
    };
    rtmMethodChannel?.invokeMethod("getPresence", args);
    RtmPresenceOhosImpl rtmPresenceOhosImpl = RtmPresenceOhosImpl(rtmMethodChannel!,rtmResultHandler);
    return rtmPresenceOhosImpl;
  }

  @override
  RtmStorage getStorage() {
    final Map<String, dynamic> args = {
    };
    rtmMethodChannel?.invokeMethod("getStorage", args);
    RtmStorageOhosImpl rtmStorageOhosImpl = RtmStorageOhosImpl(rtmMethodChannel!,rtmResultHandler);
    return rtmStorageOhosImpl;
  }

  @override
  Future<(RtmStatus, LoginResult?)> login(String token) async {
    final Map<String, dynamic> args = {
      "token": token,
    };
    var code = await rtmMethodChannel?.invokeMethod<int>("login", args) ?? 0;
    return (getRtmStatus(code),LoginResult());
  }

  @override
  Future<(RtmStatus, LogoutResult?)> logout() async {
    final Map<String, dynamic> args = {
    };
    var code = await rtmMethodChannel?.invokeMethod<int>("logout", args) ?? 0;
    return (getRtmStatus(code),LogoutResult());
  }

  @override
  Future<(RtmStatus, PublishResult?)> publish(
      String channelName, String message,
      {RtmChannelType channelType = RtmChannelType.message,
      String? customType,
      bool storeInHistory = false}) async {
    final Map<String, dynamic> option = {
      "channelType":channelType.value(),
      "customType":customType,
      "storeInHistory":storeInHistory,
    };
    final Map<String, dynamic> args = {
      "channelName": channelName,
      "message": message,
      "option": jsonEncode(option),
    };
    var code = await rtmMethodChannel?.invokeMethod<String>("publish", args);
    Map<String,dynamic> map = jsonDecode(code!);
    final (PublishResult result, RtmErrorCode errorCode) = await rtmResultHandler.request(int.parse(map['requestId']));
    return (getRtmStatus(map['errorCode']),result);
    // return (getRtmStatus(code),PublishResult());
  }

  @override
  Future<(RtmStatus, PublishResult?)> publishBinaryMessage(
      String channelName, Uint8List message,
      {RtmChannelType channelType = RtmChannelType.message,
      String? customType,
      bool storeInHistory = false}) {
    // TODO: implement publishBinaryMessage
    throw UnimplementedError();
  }

  @override
  Future<RtmStatus> release() async {
    final Map<String, dynamic> args = {
    };
    var code = await rtmMethodChannel?.invokeMethod<int>("release", args) ?? 0;
    return getRtmStatus(code);
  }

  @override
  void removeListener(
      {void Function(LinkStateEvent event)? linkState,
      void Function(MessageEvent event)? message,
      void Function(PresenceEvent event)? presence,
      void Function(TopicEvent event)? topic,
      void Function(LockEvent event)? lock,
      void Function(StorageEvent event)? storage,
      void Function(TokenEvent event)? token}) {
    _removeListenerIfNeeded('linkState', linkState);
    _removeListenerIfNeeded('message', message);
    _removeListenerIfNeeded('presence', presence);
    _removeListenerIfNeeded('topic', topic);
    _removeListenerIfNeeded('lock', lock);
    _removeListenerIfNeeded('storage', storage);
    _removeListenerIfNeeded('token', token);
  }

  @override
  Future<(RtmStatus, RenewTokenResult?)> renewToken(String token) async {
    final Map<String, dynamic> args = {
      "token":token
    };
    var code = await rtmMethodChannel?.invokeMethod<int>("renewToken", args) ?? 0;
    //todo
    return (getRtmStatus(code),RenewTokenResult(serverType: RtmServiceType.none,channelName: ""));
  }

  @override
  Future<RtmStatus> setParameters(String parameters) async {
    final Map<String, dynamic> args = {
      "parameters":parameters
    };
    var code = await rtmMethodChannel?.invokeMethod<int>("setParameters", args) ?? 0;
    return getRtmStatus(code);
  }

  @override
  Future<(RtmStatus, SubscribeResult?)> subscribe(String channelName,
      {bool withMessage = true,
      bool withMetadata = false,
      bool withPresence = true,
      bool withLock = false,
      bool beQuiet = false}) async {
    final Map<String, dynamic> options = {
      "withMessage":withMessage,
      "withMetadata":withMetadata,
      "withPresence":withPresence,
      "withLock":withLock,
    };
    final Map<String, dynamic> args = {
      "channelName":channelName,
      "options":jsonEncode(options)
    };
    var code = await rtmMethodChannel?.invokeMethod<String>("subscribe", args);
    Map<String,dynamic> map = jsonDecode(code!);
    final (SubscribeResult result, RtmErrorCode errorCode) = await rtmResultHandler.request(int.parse(map['requestId']));
    return (getRtmStatus(map['errorCode']),result);
    // return (getRtmStatus(code),SubscribeResult(channelName: channelName));
  }

  @override
  Future<(RtmStatus, UnsubscribeResult?)> unsubscribe(String channelName) async {
    final Map<String, dynamic> args = {
      "channelName":channelName,
    };
    var code = await rtmMethodChannel?.invokeMethod<int>("unsubscribe", args) ?? 0;
    return (getRtmStatus(code),UnsubscribeResult(channelName: channelName));
  }
}
